from flask import current_app, render_template, request, jsonify, g
from flask_sqlalchemy import Pagination

from info.constants import CLICK_RANK_MAX_NEWS, HOME_PAGE_MAX_NEWS
from info.models import News, Category
from info.modules.home import home_blu


# 2,用蓝图对象装饰视图函数
from info.utils.common import user_login_data
from info.utils.response_code import RET, error_map


@home_blu.route('/')
@user_login_data
def index():
    # #后续需要使用内存数据库和MySQL数据库，所以需要导入
    # sr.set("name","zs")
    # try:
    #     1 / 0
    # except BaseException as e:
    #     #日志需要APP来调用，所以需要用到应用上下文
    #     #使用flask对logging模块的升级方式来打印
    #     current_app.logger.error(e)
    #模板渲染跟路由


    #可以用装饰器来取user模型对象了
    # #用session判断用户是否登录
    # user_id = session.get("user_id")
    #
    # # 先给这个session对应的模型对象为None，好让后面即使取不出来模型对象也不会报错
    # user = None   #type:User
    # # 如果session中能够取到数据，将用户昵称和头像传到模板中进行渲染
    # if user_id:
    #     # 根据主键从数据库User表中取出用户信息
    #     user = User.query.get(user_id)


    #右边固定的信息，是根据点击量排的最多显示的新闻数是一个常量，在constants.py里有定义许多常量
    # 从数据库中News表中取出点击量最多的10条新闻（根据常量定义的决定），进行模板渲染
    rank_list = []  #type: list[News]
    #从数据库取数据要用异常捕捉
    try:
        rank_list = News.query.order_by(News.clicks.desc()).limit(CLICK_RANK_MAX_NEWS).all()
    except BaseException as e:
        #将异常打印到控制台，因为是后端渲染，所以不需要将异常json返回给前端
        current_app.logger.error(e)

    #因为前端的那些分类数据都是写的伪数据，所以需要用真实的数据库分类表的所有分类替换
    # 传入模板进行替换
    try:
        categories = Category.query.all()   #type: list[Category]
    except BaseException as e:
        #后端渲染不必返回json,只需要输出异常到控制台
        current_app.logger.error(e)
    user = g.user.to_dict() if g.user else None

    #将user模型对象和10条新闻列表传入模板中,然后进入模板接收数据
    return render_template("news/index.html",user=user,rank_list=rank_list,categories=categories)



"""标题小图标"""
@home_blu.route('/favicon.ico')
def get_favicon():
    #只要浏览器连接，就会自动调用，返回静态文件，app.send_static_file()
    return current_app.send_static_file("news/favicon.ico")

"""获取滑动新闻列表"""
@home_blu.route('/get_news_list')
def get_news_list():
    #思路
    #因为根路由的新闻各分类都可以滑动查看跟多新闻，使用的是前端的局部刷新
    #所以只需要后端提供数据，发json给前端即可，请求里面应该提供cid,当前页数，和一页显示多少条新闻（这个也可以不传，后端也可以定义固定值）
    # 因为局部刷新就是请求多少就给多少，不是把所有数据都给前端，因为这样的速度慢性能也差
    #而各分类是根据cid(分类id)来区别的，cid是News表的外键，一对多，所以接口可以共用一个

    #获取参数，cid,当前所在页数，一页显示多少条新闻
    #校验参数，是否完整，只需要校验前两个，一页显示多少新闻是可以在后端定义默认值的
    #因为json传来的数据是字符串，而三个参数都是int，所以需要进行格式转换
    #根据cid外键从News数据库中取出n条（一页的新闻数）按时间排行的当前页数的新闻
    #json返回给前端

    # 获取参数，信息不敏感，用get请求就行，cid,当前所在页数，一页显示多少条新闻
    cid = request.args.get("cid")
    cur_page = request.args.get("cur_page")
    per_count = request.args.get("per_count", HOME_PAGE_MAX_NEWS)
    # 校验参数，是否完整，只需要校验前两个，一页显示多少新闻是可以在后端定义默认值的
    if not all([cid,cur_page]):
        return jsonify(errno=RET.PARAMERR,errmsg=error_map[RET.PARAMERR])
    # 因为json传来的数据是字符串，而三个参数都是int，所以需要进行格式转换
    try:
        #避免前端传来的不能转整数，所以需要异常捕捉参数错误
        cid = int(cid)
        cur_page = int(cur_page)
        per_count = int(per_count)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
    #因为最新那一栏的cid为1，而News的模型对象并没有外键为1对应的新闻，所以要将1排除再去数据库中找
    filter_list = [News.status == 0]
    if cid != 1:
        #将恒等式存进列表中，也排除了1,
        filter_list.append(News.category_id == cid)
    # 根据cid外键从News数据库中取出n条（一页的新闻数）按时间排行的当前页数的新闻
    # 返回的是pn对象,*list:就是将list中存的东西取出来以逗号隔开，一般外面都有括号（*list）
    try:
        #当分类cid等于1的时候，就是将News所有新闻按时间排序
        pn = News.query.filter(*filter_list).order_by(News.create_time.desc()).paginate(cur_page,per_count)  # type: Pagination
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
    # json返回给前端
    #先封装成json
    data = {
        #新闻列表数据，包含的都是字典形式的新闻数据，所以News表中有转成字典的方法
        "news_list": [news.to_dict() for news in pn.items],
        #后端要把所有页数给前端，这样可以在到了最后一页的时候，前端就不再会发请求了
        "total_page": pn.pages
    }
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK],data=data)


